/*
 * Part of knowledge engineering (ke) course work, 2010/11
 */
package se.bth.ke.firstfriend.web;

import de.nixis.commons.jersey.mvc.MVCView;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import org.springframework.stereotype.Component;
import de.nixis.commons.web.base.Controller;
import de.nixis.commons.web.base.security.UserLoginManager;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import se.bth.ke.firstfriend.dto.EditUserDTO;
import se.bth.ke.firstfriend.model.User;
import se.bth.ke.firstfriend.model.validation.Validator;

/**
 *
 * @author markus.goetz, nico.rehwaldt
 */
@Component
@Path("/user")
public class UserController extends Controller {

    @PersistenceContext
    private EntityManager em;

    @Autowired
    private UserLoginManager loginManager;
    
    @Context
    private HttpServletRequest hsr;

    @GET
    @Path("/create")
    public User create() {
        return new User();
    }

    @GET
    @Path("/register")
    public Object register() {
        return new User();
    }

        @GET
    @Path("/login")
    public Object login() {
        return render("/user/login");
    }

    @POST
    @Path("/login")
    @Transactional
    public Object loginSave(@FormParam("uname") String userName,
                            @FormParam("upassword") String password) {

        User user = (User) loginManager.loginWithCredentials(userName, password);
        
        NewCookie loginCookie;
        MVCView template;

        if (user != null) {
            putUserIntoSession(user);

            template = render("/user/login/success", user);
            loginCookie = getLoginCookie(user);
        } else {
            // should indicate bad status here but if we do,
            // ajax does not work anymore
            template = render("/user/login/fail", null);
            loginCookie = getRemovedLoginCookie();
        }

        return Response
                .ok(template)
                .cookie(loginCookie)
                .build();
    }

    @GET
    @Path("/logout")
    public Object logout() {
        removeUserFromSession();
        return Response
            .ok(render("/user/logout"))
            .cookie(getRemovedLoginCookie())
            .build();
    }
    
    @POST
    @Path("/save")
    @Transactional
    public Object save(
        @FormParam("username") String userName,
        @FormParam("firstname") String firstName,
        @FormParam("lastname") String lastName,
        @FormParam("email") String email,
        @FormParam("password") String password,
        @FormParam("verification") String passwordVerification) {

        User userFromInput = new User(userName, firstName, lastName,
                                      email, password, passwordVerification);
        
        Validator.ValidationResult result = Validator.validate(userFromInput);
        
        if (result.isEmpty()) {
            em.persist(userFromInput);
            putUserIntoSession(userFromInput);
            return render("/user/register/complete", null);
        } else {
            return render("/user/register", new EditUserDTO(userFromInput, result));
        }
    }

    @GET
    @Path("/update")
    public EditUserDTO update() {
        return new EditUserDTO((User) getActiveUser(), null);
    }
    
    @POST
    @Path("/updatesave")
    public Object updatesave(@FormParam("email") String email,
                             @FormParam("password") String password,
                             @FormParam("verfication") String passwordVerfication) {

        User currentUser = (User) getActiveUser();

        try {
            User updatedUser = updateUser(currentUser, email, password, passwordVerfication);
            putUserIntoSession(updatedUser);
            
            return render("/portal/index", null);
        } catch (IllegalStateException e) {            
            return render("/user/update", currentUser);
        }
    }
    
    @Transactional
    private User updateUser(User user, String email, String password, String verification) {
        User copy = em.find(User.class, user.getId());
        
        copy.setEmail(email);
        copy.setPassword(password, verification);

        // TODO: Update user correctly in database
        Validator.ValidationResult result = validateUser(copy);
        
        if (!result.isEmpty()) {
            throw new IllegalStateException("User invalid");
        }

        return em.merge(copy);
    }

    private Validator.ValidationResult validateUser(User user) {
        Validator.ValidationResult result = Validator.validate(user);

        if (!user.verificationMatches()) {
            result.addViolation("Password and entered verfication does not match");
        }

        if (!user.isPasswordValid()) {
            result.addViolation("Password cannot be blank");
        }

        if (!isUserUnique(user)) {
            result.addViolation("Username already in use");
        }

        return result;
    }

    private void putUserIntoSession(User user) {
        hsr.getSession(true).setAttribute("currentUser", user);
    }

    private void removeUserFromSession() {
        hsr.getSession(true).removeAttribute("currentUser");
    }

    private boolean isUserUnique(User userFromInput) {
        return em.createQuery("SELECT u.name FROM User u WHERE :new_user_name IN u.name")
                               .setParameter("new_user_name", userFromInput.getName())
                               .getResultList()
                               .isEmpty();
    }

    private NewCookie getRemovedLoginCookie() {
        return new NewCookie("uid", "", "/firstfriend", null, null, 0, false);
    }
    
    private NewCookie getLoginCookie(User user) {
        return new NewCookie("uid", user.createAuthToken(), "/firstfriend", null, null, 3000, false);
    }
}
